//-------------------------------------------------------------------
//	Copyright (c) 2010-2014  Zhirnov Andrey
//	This file is part of the "UX Standard Lib" project.
//	See copyright notice in "UX_STL_Core.h".
//-------------------------------------------------------------------

#ifndef _UXS_OPTIONAL_H
#define _UXS_OPTIONAL_H

#pragma once

#include "MemFunc.h"

namespace UX_STL
{
namespace UXTypes
{

	//
	// Optional
	//

	template <typename T>
	struct TOptional
	{
	// types
	public:
		typedef TOptional< T >	Self;
		typedef T				value_t;

	private:
		typedef char	data_t[ sizeof(T) ];


	// variables
	private:
		data_t		_value;
		bool		_isDefined;


	// methods
	private:
		void _Create (const T &value)
		{
			_Destroy();
			placement_new( _value, value );
			_isDefined = true;
		}

		void _Destroy ()
		{
			if ( _isDefined )
			{
				placement_delete( Get() );
				_isDefined = false;
			}
		}

	public:
		TOptional () : _isDefined(false)
		{
			ZeroMem( _value );
		}


		explicit
		TOptional (const T &value) : _isDefined(false)
		{
			_Create( value );
		}


		TOptional (const Self &other) : _isDefined(false)
		{
			if ( other.IsDefined() )
				_Create( other.Get() );
			else
				ZeroMem( _value );
		}


		~TOptional ()
		{
			_Destroy();
		}


		Self &	operator =  (const T& value)
		{
			_Create( value );
			return *this;
		}


		bool	operator == (const Self &other) const
		{
			return IsDefined() and other.IsDefined() and Get() == other.Get();
		}


		bool	operator != (const Self &other) const
		{
			return not (*this == other);
		}


		operator bool		()		const	{ return IsDefined(); }

		T const &	operator * ()	const	{ return Get(); }
		T &			operator * ()			{ return Get(); }

		T *			operator -> ()			{ return GetPtr(); }
		T const *	operator -> ()	const	{ return GetPtr(); }

		bool		IsDefined ()	const	{ return _isDefined; }

		T const &	Get ()			const	{ return *GetPtr(); }
		T &			Get ()					{ return *GetPtr(); }

		T *			GetPtrOrNull ()			{ return IsDefined() ? GetPtr() : null; }
		T const *	GetPtrOrNUll ()	const	{ return IsDefined() ? GetPtr() : null; }


		T const *	GetPtr ()		const
		{
			ASSERT( IsDefined() );
			return (T const *) _value;
		}


		T *			GetPtr ()
		{
			ASSERT( IsDefined() );
			return (T *) _value;
		}


		void		Undefine ()
		{
			_Destroy();
		}
	};

}	// UXTypes
}	// UX_STL

#endif	// _UXS_OPTIONAL_H